#include "dxframe.h"
#include <cassert>

class cD3D9Enumer;
struct cDlgState;
cD3D9Enumer* pD3D9Enumer = GetEnumer();
cDlgState* pDlgState = GetDlgState();

cD3DFrame::cD3D::cD3D()
{
	m_D3DDeviceSettings.m_nAdapterOrdinal = D3DADAPTER_DEFAULT;
	m_D3DDeviceSettings.m_DeviceType = D3DDEVTYPE_HAL;
	m_D3DDeviceSettings.m_AdapterFmt = D3DFMT_X8R8G8B8;
	m_D3DDeviceSettings.m_dwBehaviorFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING;

	m_D3DDeviceSettings.m_D3DPP.BackBufferCount = 1;
	m_D3DDeviceSettings.m_D3DPP.BackBufferFormat = D3DFMT_X8R8G8B8;
	m_D3DDeviceSettings.m_D3DPP.BackBufferWidth = DEFAULTCLIENTWIDTH;
	m_D3DDeviceSettings.m_D3DPP.BackBufferHeight = DEFAULTCLIENTHEIGHT;
	m_D3DDeviceSettings.m_D3DPP.MultiSampleType = D3DMULTISAMPLE_NONE;
	m_D3DDeviceSettings.m_D3DPP.MultiSampleQuality = 0;
	m_D3DDeviceSettings.m_D3DPP.SwapEffect = D3DSWAPEFFECT_DISCARD;
	m_D3DDeviceSettings.m_D3DPP.Windowed = true;
	m_D3DDeviceSettings.m_D3DPP.EnableAutoDepthStencil = true;
	m_D3DDeviceSettings.m_D3DPP.AutoDepthStencilFormat = D3DFMT_D24S8;
	m_D3DDeviceSettings.m_D3DPP.Flags = 0;
	m_D3DDeviceSettings.m_D3DPP.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
	m_D3DDeviceSettings.m_D3DPP.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
	
	m_Display = false;
	
}
HRESULT cD3DFrame::OnCreateWindow(WCHAR* wcWindowTitle,HINSTANCE hInstance,HICON hIcon,int nX,int nY)
{
	WNDCLASS wndclass;
	wndclass.style = CS_VREDRAW | CS_HREDRAW;
	wndclass.lpfnWndProc = WndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = hInstance;
	wndclass.hIcon = hIcon;
	wndclass.hCursor = LoadCursor(NULL,IDC_ARROW);
	wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = L"DirectXWindowClass";
	DWORD dwResult = S_OK;
	if(!RegisterClass(&wndclass)){
		dwResult = GetLastError();
		TRACE("RegisterClass Failed\n");
		return HRESULT_FROM_WIN32(dwResult);
	}

	RECT wndRect = {0,0,DEFAULTCLIENTWIDTH,DEFAULTCLIENTHEIGHT};
	AdjustWindowRect(&wndRect,WS_OVERLAPPEDWINDOW,false);
	HWND hWnd = CreateWindow(
				L"DirectXWindowClass",wcWindowTitle,WS_OVERLAPPEDWINDOW,
				nX,nY,wndRect.right - wndRect.left,wndRect.bottom - wndRect.top,
				NULL,NULL,hInstance,NULL);
	if(hWnd == NULL){
		dwResult = GetLastError();
		TRACE("CreateWindow Failed\n");
		return HRESULT_FROM_WIN32(dwResult);
	}
	SetHWND(hWnd);

	return dwResult;
}

bool cD3DFrame::OnCreateD3DDevice()
{
	LPDIRECT3D9 pD3D9 = NULL;
	pD3D9 = GetD3D9();
	pD3D9 = Direct3DCreate9(D3D_SDK_VERSION);
	if(pD3D9 == NULL){
		TRACE("Direct3DCreate9 Failed\n");
		return false;
	}
	SetD3D9(pD3D9);
	//check the Device
	CheckDevice();
	CreateOwnDialog((HINSTANCE)GetModuleHandle(NULL),GetHWND());
	return GetUserDeviceSetting();
}

bool cD3DFrame::IsDeviceLost()
{
	LPDIRECT3DDEVICE9 pD3DDEVICE9 = GetD3DDEVICE9();
	HRESULT hr = pD3DDEVICE9->TestCooperativeLevel();
	switch(hr)
	{
	case D3D_OK:
		break;
	case D3DERR_DEVICELOST:
		Sleep(10);
		return true;
	case D3DERR_DEVICENOTRESET:
		OnLostDevice();
		pD3DDEVICE9->Reset(&m_D3DState.m_D3DDeviceSettings.m_D3DPP);
		OnResetDevice();
		break;
	case D3DERR_DRIVERINTERNALERROR:
		PostQuitMessage(0);
		return false;
	}
	pD3DDEVICE9 = NULL;
	return true;
}
void cD3DFrame::SetOnInit(fpOnInit _fpOnInit)
{
	assert(_fpOnInit!=NULL);
	OnInit = _fpOnInit;
}
void cD3DFrame::SetLostDevice(fpOnLostDevice _fpOnLostDevice)
{
	assert(_fpOnLostDevice!=NULL);
	OnLostDevice = _fpOnLostDevice;
}
void cD3DFrame::SetResetDevice(fpOnResetDevice _fpOnResetDevice)
{
	assert(_fpOnResetDevice!=NULL);
	OnResetDevice = _fpOnResetDevice;
}
void cD3DFrame::SetUpdateFunc(fpUpdate _fpUpdate)
{
	assert(_fpUpdate!=NULL);
	Update = _fpUpdate;
}
void cD3DFrame::UpdateToDt()
{
	float dt = m_TimeCounter->GetElapsedTime();
	SetFPS(m_TimeCounter->GetFPS());
	Update(dt);
}
HRESULT cD3DFrame::OnDraw(fpRender Render)
{
	LPDIRECT3DDEVICE9 pDevice9 = GetD3DDEVICE9();
	HR(pDevice9->Clear(0,0,D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER|D3DCLEAR_STENCIL,
		D3DCOLOR_XRGB(100,100,100),1.0f,0));

	cFontMgr* pFontMgr = GetFontMgr();
	cDXFont* pFont = pFontMgr->GetFont("default");
	//print out the fps
	RECT fpsRect = {0,0,FPSRECTWIDTH,FPSRECTHEIGHT};
	WCHAR buffer[256];
	swprintf_s(buffer,TEXT("FPS = %.1f\n"),GetFPS());
	pDevice9->BeginScene();
	pFont->DrawText(buffer,&fpsRect,DT_LEFT,0xffffffff);
	pDevice9->EndScene();
	
	HRESULT hr = Render();

	HR(pDevice9->Present(0,0,0,0));

	return hr;
}

void cD3DFrame::MainLoop(fpRender Render)
{
	MSG msg;
	msg.message = WM_NULL;
	PeekMessage(&msg,NULL,0U,0U,PM_NOREMOVE);
	OnInit();
	if(m_D3DState.m_Display){
		SetFocus(m_D3DState.m_HWND);
		ShowWindow(GetHWND(),SW_SHOW);
	}
	while(msg.message!=WM_QUIT){
		if(PeekMessage(&msg,NULL,0U,0U,PM_REMOVE)){
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else{
			m_TimeCounter->QueryPreCount();
			IsDeviceLost();
			UpdateToDt();
			OnDraw(Render);
			m_TimeCounter->QueryAftCount();
		}
	}
}
void cD3DFrame::Release(fpRelease _Release)
{
	LPDIRECT3D9 pD3D9 = GetD3D9();
	LPDIRECT3DDEVICE9 pD3DDEVICE9 = GetD3DDEVICE9();
	SAFE_RELEASE(pD3D9);
	SAFE_RELEASE(pD3DDEVICE9);
	delete m_TimeCounter;

	_Release();
}

void cD3DFrame::CheckDevice()
{
	//Get All the Device's Capabilities
	pD3D9Enumer->EnumAdapters(GetD3D9());
}
bool cD3DFrame::GetUserDeviceSetting()
{
	LPDIRECT3D9 pD3D9 = GetD3D9();
	HRESULT hr = S_OK;
	cD3DDeviceSettings* pD3DDeviceSettings = GetD3DDeviceSettings();

	D3DPRESENT_PARAMETERS* pD3DPP = &pD3DDeviceSettings->m_D3DPP;
	
	pD3DPP->hDeviceWindow = GetHWND();
	pD3DPP->BackBufferFormat = pDlgState->bbfmt;
	pD3DPP->MultiSampleType = pDlgState->MSType;
	pD3DPP->MultiSampleQuality = pDlgState->Quality;
	pD3DPP->AutoDepthStencilFormat = pDlgState->dsfmt;
	pD3DPP->Windowed = pDlgState->bWndState;
	pD3DDeviceSettings->m_dwBehaviorFlags = pDlgState->dwVPFlags;
	
	LPDIRECT3DDEVICE9 pD3DDevice9 = NULL;

	hr = pD3D9->CreateDevice(pD3DDeviceSettings->m_nAdapterOrdinal,pD3DDeviceSettings->m_DeviceType,
		GetHWND(),pD3DDeviceSettings->m_dwBehaviorFlags,pD3DPP,&pD3DDevice9);
	if(FAILED(hr)){
		TRACE("CreateDevice Failed\n");
		return false;
	}
	SetD3DDEVICE9(pD3DDevice9);
	SetDisplay(true);
	pD3DDevice9 = NULL;
	return true;
}

//cTimeCounter

void cTimeCounter::QueryPreCount()
{
	QueryPerformanceCounter((LARGE_INTEGER*)&preCount);
	QueryPerformanceFrequency((LARGE_INTEGER*)&cntsPersec);
	secsPercnt = 1.0f / cntsPersec;
}
void cTimeCounter::QueryAftCount()
{
	QueryPerformanceCounter((LARGE_INTEGER*)&aftCount);
	elapsedTime = secsPercnt*(aftCount - preCount);
	FPS = 1.0f / elapsedTime;
}
